package cn.yangliu.nacos.oauth2.config;

import cn.yangliu.nacos.oauth2.service.UserService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler;
import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

/**
 * The interface O auth 2 authorization server config.
 *
 * @author 问道于盲
 * @date 2019 -03-26
 */
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig
        extends AuthorizationServerConfigurerAdapter
        implements InitializingBean {

    /**
     * current class instance's member.
     * The Authentication manager.
     */
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private RandomValueAuthorizationCodeServices randomValueAuthorizationCodeServices;
    /**
     * current class instance's member.
     * The User service.
     */
    @Autowired
    private UserService userService;

    /**
     * current class instance's member.
     * The Client details service.
     */
    @Autowired
    private ClientDetailsService clientDetailsService;

    /**
     * current class instance's member.
     * The Token store.
     */
    @Autowired
    private TokenStore tokenStore;

    /**
     * current class instance's member.
     * The Jwt access token converter.
     */
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    /** Custom authorization and error page. */
    @Autowired
    private AuthorizationEndpoint authorizationEndpoint;


    /**
     * Configure.
     *
     * @param clients the clients
     * @throws Exception the exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetailsService);
    }

    /**
     * User approval handler token store user approval handler.
     *
     * @return the token store user approval handler
     */
    @Bean
    public TokenStoreUserApprovalHandler userApprovalHandler() {
        TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
        handler.setTokenStore(tokenStore);
        handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
        handler.setClientDetailsService(clientDetailsService);
        return handler;
    }


    /**
     * current class instance's member.
     * The Translator.
     */
    @Autowired
    private Oauth2WebResponseExceptionTranslator translator;

    /**
     * Configure.
     *
     * @param endpoints the endpoints
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.setClientDetailsService(clientDetailsService);
        endpoints.tokenStore(tokenStore)
                .authenticationManager(authenticationManager)
                .userDetailsService(userService)
                .authorizationCodeServices(randomValueAuthorizationCodeServices)
                .exceptionTranslator(translator);
        if (jwtAccessTokenConverter != null) {
            endpoints.accessTokenConverter(jwtAccessTokenConverter);
        }

    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer)
            throws Exception {
        oauthServer.tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()");
        /**
         * Allow
         * /oauth/token?grant_type=client_credentials&client_id=id&client_secret=password
         */
        oauthServer.allowFormAuthenticationForClients();
    }

    /**
     * Approval store approval store.
     *
     * @return the approval store
     */
    @Bean
    public ApprovalStore approvalStore() {
        TokenApprovalStore store = new TokenApprovalStore();
        store.setTokenStore(tokenStore);
        return store;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        authorizationEndpoint
                .setUserApprovalPage("forward:/oauth/custom_approval_page");
        authorizationEndpoint.setErrorPage("forward:/oauth/custom_error_page");
    }
}

